/*
Copyright 2010  Murali Devi (profdevi@gmail.com)

This file is part of ComineGL Project.

ComineGL is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

ComineGL is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with ComineGL.  If not, see <http://www.gnu.org/licenses/>.

This code uses the irrlicht and irrklang libraries.  Please read
the libraries copyrights at 

	irrlicht library:  http://irrlicht.sourceforge.net
	irrklang library:  http://www.ambiera.com/irrklang

*/



//v1.0 copyright Comine.com 20101115M03
#ifndef TIntRefCountMap_h
#define TIntRefCountMap_h

//////////////////////////////////////////////////
#include "MStdLib.h"
#include "MIntList.h"
#include "TRefCount.h"

//////////////////////////////////////////////////
template<class ValueClass>
class TIntRefCountMap
	{
	//////////////////////////////////////////////
	struct GNode
		{
		int Key;
		TRefCount<ValueClass> *RefObject;
		unsigned int Counter;					// Holds count of times that this key was searched for
		GNode *Next;
		};


	//////////////////////////////////////////////
	int mNodeCount;
	GNode *mHeadNode;

	//////////////////////////////////////////////
	bool ClearObject(void)
		{
		mNodeCount=0;
		mHeadNode=NULL;
		return true;
		}

	/////////////////////////////////////////////
	public:
	TIntRefCountMap(bool create=false)
		{
		ClearObject();
		if(create==true && Create()==false)
			{
			return;
			}
		}

	////////////////////////////////////////////
	~TIntRefCountMap(void)
		{
		Destroy();
		}


	///////////////////////////////////////////
	bool Create(void)
		{
		Destroy();
		
		mHeadNode=new GNode;
		if(mHeadNode==NULL)
			{
			Destroy();
			return false;
			}

		// Init Members of node
		mHeadNode->Key=0;
		mHeadNode->Next=NULL;
		mHeadNode->RefObject=NULL;
		mHeadNode->Counter=0;
		mNodeCount=0;

		return true;
		}
	
	///////////////////////////////////////////
	bool Destroy(void)
		{
		GNode *p;
		GNode *tmpnext;
		for(p=mHeadNode;p!=NULL;p=tmpnext)
			{
			tmpnext=p->Next;
			p->Key=0;
			p->Next=NULL;

			if(p->RefObject!=NULL)
				{
				p->RefObject->Drop();
				p->RefObject=NULL;
				}

			p->Counter=0;
			delete p;
			}

		ClearObject();
		return true;
		}

	///////////////////////////////////////////
	int GetCount(void)
		{
		return mNodeCount;
		}

	///////////////////////////////////////////
	bool Print(void)
		{
		MStdPrintf("TIntRefCountMap: (%d Keys)\n",mNodeCount );
		
		GNode *p;
		for(p=mHeadNode->Next;p!=NULL;p=p->Next)
			{
			int refcount=0;
			if(p->RefObject!=NULL)
				{
				refcount = p->RefObject->GetRefCount();
				}

			MStdPrintf("Key=%-6d\tValue=%08x\tAccess Count=%-6d\tRefCount=%d\n"
					,p->Key,p->RefObject,p->Counter,refcount);
			}

		MStdPrintf("\n");
		return true;
		}

	////////////////////////////////////////////
	bool Add(int key,TRefCount<ValueClass> *refptr)
		{
		GNode *newnode=new GNode;
		if(newnode==NULL)
			{
			return false;
			}

		newnode->Counter=0;
		newnode->Key=key;
		newnode->RefObject= refptr;
		refptr->Grab();

		// Add to head of list
		newnode->Next = mHeadNode->Next;
		mHeadNode->Next=newnode;

		// Increase the count
		mNodeCount = mNodeCount + 1;

		return true;
		}

	//////////////////////////////////////////
	bool Remove(int key)
		{

		GNode *p;
		for(p=mHeadNode;p->Next!=NULL;p=p->Next)
			{
			if(p->Next->Key!=key)
				{
				continue;
				}

			//=We Found the key to remove
			GNode *thenode=p->Next;

			p->Next = thenode->Next;
	
			thenode->Counter=0;
			thenode->Key=0;
			thenode->Next=NULL;
			
			// Drop the reference count
			if(thenode->RefObject!=NULL)
				{
				thenode->RefObject->Drop();
				thenode->RefObject=NULL;
				}

			delete thenode;

			mNodeCount = mNodeCount - 1;
			return true;
			}
		
		//=The key is not in list

		return false;
		}

	//////////////////////////////////////////
	bool IsKey(int key)
		{
		GNode *p;
		for(p=mHeadNode;p->Next!=NULL;p=p->Next)
			{
			if(p->Next->Key!=key)
				{
				continue;
				}

			//=We Found the key.

			// Move Node to Front of list
			GNode *thenode=p->Next;
			p->Next=thenode->Next;
			thenode->Next=mHeadNode->Next;
			mHeadNode->Next=thenode;

			// Increase the count of found keys
			thenode->Counter = thenode->Counter + 1;
			return true;
			}
		
		return false;
		}

	/////////////////////////////////////////
	TRefCount<ValueClass> *Find(int key)
		{
		if(IsKey(key)==false)
			{
			return NULL;
			}

		GNode *p;
		for(p=mHeadNode->Next;p!=NULL;p=p->Next)
			{
			if(p->Key==key)
				{
				return p->RefObject;
				}
			}

		return NULL;
		}

	/////////////////////////////////////////
	bool Clear(void)
		{
		GNode *p;
		GNode *nextnode;
		for(p=mHeadNode->Next;p!=NULL;p=nextnode)
			{
			nextnode=p->Next;
			p->Next=NULL;
			p->Counter=0;
			p->Key=0;

			if(p->RefObject!=NULL)
				{
				p->RefObject->Drop();
				p->RefObject=NULL;
				}

			delete p;
			}

		mHeadNode->Next=NULL;
		mNodeCount=0;
		return true;
		}

	/////////////////////////////////////////
	bool UpdateValue(int key,TRefCount<ValueClass> *refptr)
		{
		MStdAssert(refptr!=NULL && refptr->GetRefCount()>0);
		if(IsKey(key)==false)
			{
			return false;
			}

		GNode *p;
		for(p=mHeadNode->Next;p!=NULL;p=p->Next)
			{
			if(p->Key==key)
				{
				p->RefObject->Drop();
				p->RefObject=refptr;
				p->RefObject->Grab();
				return true;
				}
			}

		return false;
		}

	///////////////////////////////////////////////
	bool GetKeys(MIntList &list)
		{
		if(list.Create()==false)
			{
			return false;
			}

		GNode *p;
		for(p=mHeadNode->Next;p!=NULL;p=p->Next)
			{
			if(list.Add(p->Key)==false)
				{
				return false;
				}
			}

		return true;
		}
	};


#endif // TIntRefCountMap